#!/usr/bin/env python

# Spit out network configuration stuff

from optparse import OptionParser

def split_combined(addr): 
    return [addr[:8]] + [addr[8:16]] + [addr[16:24]] + [addr[24:32]]
    
def from_bin_to_normal(addr): 
    return [ str( int(each, 2) ) for each in addr ]
    
def to_split_bin(split_addr): 
    split_bin_addr = [ ( bin( int(each) )[2:] ).rjust(8, '0') for each in split_addr ]
    return split_bin_addr

def make_network_and_broadcast(combined_bin_ip, host_mask): 
    end = -1 * host_mask
    combined_bin_broadcast = combined_bin_ip[:end] + (host_mask * '1')
    combined_bin_network = combined_bin_ip[:end] + (host_mask * '0')
    
    split_bin_broadcast = split_combined(combined_bin_broadcast)
    split_bin_network = split_combined(combined_bin_network) 
    
    return split_bin_broadcast, split_bin_network

def make_gateway_start_addr_end_addr(split_network, split_broadcast): 
    network_last_octet = int(split_network[-1])
    broadcast_last_octet = int(split_broadcast[-1])
    
    split_gateway = split_network[:-1] + [ str(network_last_octet + 1) ]
    split_start_addr = split_network[:-1] + [ str(network_last_octet + 2) ]
    
    split_end_addr = split_broadcast[:-1] + [ str(broadcast_last_octet - 1) ]
    
    return split_gateway, split_start_addr, split_end_addr

def merge(addr): 
    return '.'.join(addr)

def make_centos_range(start_clone_num, start_addr, end_addr, netmask, assignable_addresses): 
    next_clone_num = start_clone_num + assignable_addresses 
    range_file = "CLONENUM_START=%s\nIPADDR_START=%s\nIPADDR_END=%s\nNETMASK=%s\n# NEXT: %s" % (
        start_clone_num, start_addr, end_addr, netmask, next_clone_num
    )
    return range_file, next_clone_num

def add_binary_to_int(binary_number, integer): 
    return ( bin( int(binary_number, 2) + integer )[2:] ).rjust(32, '0')
    
def make_windows_range(combined_bin_broadcast, combined_bin_network, gateway, interface, netmask, start_addr, is_primary): 
    if is_primary: 
        addresses = '\nnetsh interface ip set address "%s" static %s %s\n\n' % (interface, start_addr, netmask) 
        addresses += 'route -P add 0.0.0.0 mask 0.0.0.0 %s\n\n' % gateway
        skip = 3
    else: 
        addresses = ''
        skip = 2
    
    combined_bin_address_book = []
    
    for each in range( int(combined_bin_network, 2) + skip, int(combined_bin_broadcast, 2) ): 
        combined_bin_address_book += [ bin(each)[2:].rjust(32, '0') ]
    
    split_bin_address_book = [ split_combined( each ) for each in combined_bin_address_book ]
    split_normal_address_book = [ from_bin_to_normal( each ) for each in split_bin_address_book ]
    combined_normal_address_book = [ merge(each) for each in split_normal_address_book ]
    
    for each in combined_normal_address_book: 
        addresses += 'netsh interface ip add address "%s" %s %s\n' % (interface, each, netmask)
    return addresses
    
class IPBlock: 
    def __init__(self, network_block, interface = 'wan', is_primary = True, start_clone_num = 2): 
        ip, cidr_mask = network_block.strip().split('/')
        
        int_cidr_mask = int(cidr_mask)
        
        host_mask = 32 - int_cidr_mask
        
        total_addresses = 2 ** host_mask
        
        routable_addresses = total_addresses - 2
        
        assignable_addresses = total_addresses - 3
        
        zeroes = host_mask * '0'
        
        combined_bin_mask = int_cidr_mask * '1' + zeroes
        
        host_bits = len(zeroes) 
        
        split_bin_mask = split_combined(combined_bin_mask)
        
        bin_mask = merge(split_bin_mask)
        
        split_netmask = from_bin_to_normal(split_bin_mask)
        
        netmask = merge(split_netmask)
        
        split_ip = ip.split('.')
        
        last_octet = split_ip[-1]
        
        int_last_octet = int(last_octet)
        
        split_bin_ip = to_split_bin(split_ip)
        
        combined_bin_ip = ''.join(split_bin_ip)
        
        split_bin_broadcast, split_bin_network = make_network_and_broadcast(combined_bin_ip, host_mask)
        
        split_broadcast = from_bin_to_normal(split_bin_broadcast)
        
        split_network = from_bin_to_normal(split_bin_network)
        
        split_gateway, split_start_addr, split_end_addr = make_gateway_start_addr_end_addr(split_network, split_broadcast) 
        
        network, gateway, start_addr, end_addr, broadcast = [ 
            merge(each) for each in [ 
                split_network, split_gateway, split_start_addr, split_end_addr, split_broadcast
            ] 
        ]
        
        combined_bin_broadcast = ''.join(split_bin_broadcast)
        combined_bin_network = ''.join(split_bin_network)
        
        centos_range, next_clone_num = make_centos_range(
            start_clone_num, start_addr, end_addr, netmask, assignable_addresses
        )
        
        windows_range = make_windows_range(
            combined_bin_broadcast, combined_bin_network, gateway, interface, netmask, start_addr, is_primary
        )
        
        self.ip = ip
        
        self.network = network
        
        self.netmask = netmask
        
        self.gateway = gateway
        
        self.start_addr = start_addr
        
        self.end_addr = end_addr
        
        self.broadcast = broadcast
        
        self.centos_range = centos_range
        
        self.next_clone_num = next_clone_num
        
        self.windows_range = windows_range

def main(): 
    parser = OptionParser(description = 'An IP calculator.', 
        epilog = "Version 0.1 merely created CentOS ifcfg-ethX-rangeY files. This does more.",
        usage = "%prog -ibmnsge --centos-range 10.20.30.40/8 ...", 
        version = '0.3'
    )
    
    parser.add_option('-a', '--ip-address',
        default = False,
        action = "store_true", 
        help = "Print the IP address."
    )
    
    parser.add_option('-b', '--broadcast', 
        default = False,
        action = "store_true",
        help = 'Print broadcast address.'
    )
    
    parser.add_option('-i', '--interface',
        default = 'wan', 
        action = 'store', 
        help = 'Interface to configure IPs on.'
    )
    
    parser.add_option('-m', '--netmask',
        default = False,
        action = "store_true", 
        help = "Print network mask (netmask)."
    )
    
    parser.add_option('-n', '--network',
        default = False,
        action = "store_true", 
        help = "Print network address."
    )

    parser.add_option('-s', '--start-addr',
        default = False,
        action = "store_true", 
        help = "Print first usable address."
    )

    parser.add_option('-g', '--gateway',
        default = False,
        action = "store_true", 
        help = "Print gateway address."
    )

    parser.add_option('-e', '--end-addr',
        default = False,
        action = "store_true", 
        help = "Print last usable address."
    )
    
    parser.add_option('-w', '--windows-range',
        default = False,
        action = "store_true",
        help = "Print Windows range configuration."
    )
    
    parser.add_option('--centos-range',
        default = False,
        action = "store_true", 
        help = "Output CentOS range configuration."
    )
    
    (options, args) = parser.parse_args()
    
    if not args: 
        print parser.print_help()
        
    if options.centos_range: 
        ip_blocks = [ IPBlock(args[0]) ]
        if len(args) > 1: 
            for each in args[1:]: 
                ip_blocks += [ IPBlock( each, start_clone_num = (ip_blocks[-1]).next_clone_num ) ]
                (ip_blocks[-2]).centos_range += '\n'
    else: 
        ip_blocks = [ IPBlock(each) for each in args ]

    if options.windows_range: 
        ip_blocks = [ IPBlock(args[0], interface = options.interface) ]
        if len(args) > 1: 
            ip_blocks += [ IPBlock(each, is_primary = False, interface = options.interface) for each in args[1:] ]
        
    for each in ip_blocks: 
        if options.broadcast: print each.broadcast
        if options.ip_address: print each.ip_address
        if options.netmask: print each.network
        if options.netmask: print each.netmask
        if options.start_addr: print each.start_addr
        if options.gateway: print each.gateway
        if options.end_addr: print each.end_addr
        if options.centos_range: print each.centos_range
        if options.windows_range: print each.windows_range
    
if __name__ == '__main__': 
    main()
